﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Fast.Framework.Interfaces;
using Fast.Framework.Extensions;
using Fast.Framework.Models;
using System.IO;
using System.Data;

namespace Fast.Framework
{

    /// <summary>
    /// 数据库上下文实现类
    /// </summary>
    /// <typeparam name="TDbOptions"></typeparam>
    public class DbContext<TDbOptions> : IDbContext<TDbOptions> where TDbOptions : DbOptions
    {

        /// <summary>
        /// Ado
        /// </summary>
        public IAdo<TDbOptions> Ado { get; }

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="ado">ado</param>
        public DbContext(IAdo<TDbOptions> ado)
        {
            Ado = ado;
        }

        #region 增 删 改
        /// <summary>
        /// 插入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public IInsert<T, TDbOptions> Insert<T>(T entity) where T : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }
            var keyValues = entity.GetPropertyValue(true);
            if (keyValues.Count == 0)
            {
                throw new Exception($"{nameof(entity)}未获取到任何属性");
            }
            return Insert<T>(keyValues);
        }

        /// <summary>
        /// 插入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entityList">实体列表</param>
        /// <returns></returns>
        public IInsert<T, TDbOptions> Insert<T>(List<T> entityList) where T : class
        {
            if (entityList == null)
            {
                throw new ArgumentNullException(nameof(entityList));
            }
            if (!entityList.Any())
            {
                throw new Exception($"{nameof(entityList)}不存在任何元素");
            }
            var type = typeof(T);
            var insertBuilder = new InsertBuilder();
            insertBuilder.IsBatch = true;
            insertBuilder.TableName = Ado.Identifier.Insert(1, type.GetTableName());
            var listKeyValues = entityList.GetPropertyValue(true);
            insertBuilder.ColumnString = string.Join(",\r\n", listKeyValues[0].Keys.Select(s => Ado.Identifier.Insert(1, s)));
            insertBuilder.ValueString = string.Join(",\r\n", listKeyValues[0].Keys.Select(s => $"{Ado.ParameterNotation}{s}"));
            insertBuilder.ListParameters = listKeyValues;
            return new Insert<T, TDbOptions>(Ado, insertBuilder);
        }

        /// <summary>
        /// 插入
        /// </summary>
        /// <param name="keyValues">键值</param>
        /// <returns></returns>
        public IInsert<T, TDbOptions> Insert<T>(Dictionary<string, object> keyValues) where T : class
        {
            if (keyValues == null)
            {
                throw new ArgumentNullException(nameof(keyValues));
            }
            if (keyValues.Count == 0)
            {
                throw new Exception($"{nameof(keyValues)}个数不能为零");
            }
            var type = typeof(T);
            var insertBuilder = new InsertBuilder();
            insertBuilder.TableName = Ado.Identifier.Insert(1, type.GetTableName());
            insertBuilder.ColumnString = string.Join(",\r\n", keyValues.Keys.Select(s => Ado.Identifier.Insert(1, s)));
            insertBuilder.ValueString = string.Join(",\r\n", keyValues.Keys.Select(s => $"{Ado.ParameterNotation}{s}"));
            insertBuilder.Parameters = keyValues;
            return new Insert<T, TDbOptions>(Ado, insertBuilder);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IDelete<T, TDbOptions> Delete<T>() where T : class
        {
            var deleteBuilder = new DeleteBuilder();
            var type = typeof(T);
            deleteBuilder.TableName = Ado.Identifier.Insert(1, type.GetTableName());
            return new Delete<T, TDbOptions>(Ado, deleteBuilder);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">对象(这是一个虚拟对象主要是为了匿名类型可以使用Where方法)</param>
        /// <returns></returns>
        public IDelete<T, TDbOptions> Delete<T>(T obj) where T : class
        {
            if (obj == null)
            {
                throw new ArgumentNullException(nameof(obj));
            }
            var deleteBuilder = new DeleteBuilder();
            var type = typeof(T);
            deleteBuilder.TableName = Ado.Identifier.Insert(1, type.GetTableName());
            return new Delete<T, TDbOptions>(Ado, deleteBuilder);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        public IUpdate<T, TDbOptions> Update<T>(T entity) where T : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }
            var keyValues = entity.GetPropertyValue(true);
            if (keyValues.Count == 0)
            {
                throw new Exception($"{nameof(entity)}未获取到任何属性");
            }
            return Update<T>(keyValues);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entityList">实体列表</param>
        /// <returns></returns>
        public IUpdate<T, TDbOptions> Update<T>(List<T> entityList) where T : class
        {
            if (entityList == null)
            {
                throw new ArgumentNullException(nameof(entityList));
            }
            if (!entityList.Any())
            {
                throw new Exception($"{nameof(entityList)}不存在任何元素");
            }
            var type = typeof(T);
            var updateBuilder = new UpdateBuilder();
            updateBuilder.IsBatch = true;
            updateBuilder.TableName = Ado.Identifier.Insert(1, type.GetTableName());

            var listKeyValues = entityList.GetPropertyValue();
            updateBuilder.ListParameters = listKeyValues;

            var keys = type.GetPrimaryKey();
            updateBuilder.SetString = string.Join(",", listKeyValues[0].Where(w => !keys.Exists(e => e == w.Key)).Select(s => $"{Ado.Identifier.Insert(1, s.Key)} = {Ado.ParameterNotation}{s.Key}"));
            if (keys.Count > 0)
            {
                foreach (var key in keys)
                {
                    updateBuilder.Where.Add($"{Ado.Identifier.Insert(1, key)} = {Ado.ParameterNotation}{key}");//主键
                }
            }
            return new Update<T, TDbOptions>(Ado, updateBuilder);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="keyValues">键值</param>
        /// <returns></returns>
        public IUpdate<T, TDbOptions> Update<T>(Dictionary<string, object> keyValues) where T : class
        {
            if (keyValues == null)
            {
                throw new ArgumentNullException(nameof(keyValues));
            }
            if (keyValues.Count == 0)
            {
                throw new Exception($"{nameof(keyValues)}个数不能为零");
            }
            var type = typeof(T);
            var updateBuilder = new UpdateBuilder();
            updateBuilder.TableName = Ado.Identifier.Insert(1, type.GetTableName());
            updateBuilder.SetString = string.Join(",", keyValues.Select(s => $"{Ado.Identifier.Insert(1, s.Key)} = {Ado.ParameterNotation}{s.Key}"));
            updateBuilder.Parameters = keyValues;
            return new Update<T, TDbOptions>(Ado, updateBuilder);
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression">表达式</param>
        /// <returns></returns>
        public IUpdate<T, TDbOptions> Update<T>(Expression<Func<T, T>> expression) where T : class
        {
            var type = typeof(T);
            var updateBuilder = new UpdateBuilder();
            updateBuilder.TableName = Ado.Identifier.Insert(1, type.GetTableName());
            var sqlInfo = expression.ResolveSql(new SqlExpressionOptions()
            {
                IgnoreParameterExpression = true,
                ParameterNotation = Ado.ParameterNotation,
                Identifier = Ado.Identifier
            });
            updateBuilder.SetString = string.Join(",", sqlInfo.NewAssignMapper);
            updateBuilder.Parameters = sqlInfo.SqlParameters;
            return new Update<T, TDbOptions>(Ado, updateBuilder);
        }
        #endregion

        #region 大数据
        /// <summary>
        /// 大数据
        /// </summary>
        /// <returns></returns>
        public IBigData<TDbOptions> BigData()
        {
            return new BigData<TDbOptions>(Ado);
        }
        #endregion

        #region 查询
        /// <summary>
        /// 查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IQuery<T, TDbOptions> Query<T>() where T : class, new()
        {
            var queryBuilder = new QueryBuilder();
            queryBuilder.TableName = $"{Ado.Identifier.Insert(1, typeof(T).GetTableName())} p0";
            queryBuilder.Columns = "*";
            return new Query<T, TDbOptions>(Ado, queryBuilder);
        }

        /// <summary>
        /// 联合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="querys">查询</param>
        /// <returns></returns>
        public IQuery<T, TDbOptions> Union<T>(params IQuery<T, TDbOptions>[] querys) where T : class
        {
            return Union(querys.ToList());
        }

        /// <summary>
        /// 联合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="querys">查询</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public IQuery<T, TDbOptions> Union<T>(List<IQuery<T, TDbOptions>> querys) where T : class
        {
            if (querys == null || querys.Count == 0)
            {
                throw new ArgumentNullException(nameof(querys));
            }
            var queryBuilder = new QueryBuilder();
            queryBuilder.TableName = Ado.Identifier.Insert(1, $"UnionTable{querys.Count}");
            for (int i = 0; i < querys.Count; i++)
            {
                queryBuilder.Union.Add(querys[i].ToString());
                queryBuilder.Parameters.Append(querys[i].QueryBuilder.Parameters);
            }
            return new Query<T, TDbOptions>(Ado, queryBuilder);
        }

        /// <summary>
        /// 联合所有
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="querys">查询</param>
        /// <returns></returns>
        public IQuery<T, TDbOptions> UnionAll<T>(params IQuery<T, TDbOptions>[] querys) where T : class
        {
            return UnionAll(querys.ToList());
        }

        /// <summary>
        /// 联合所有
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="querys">查询</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public IQuery<T, TDbOptions> UnionAll<T>(List<IQuery<T, TDbOptions>> querys) where T : class
        {
            if (querys == null || querys.Count == 0)
            {
                throw new ArgumentNullException(nameof(querys));
            }
            var queryBuilder = new QueryBuilder();
            queryBuilder.TableName = Ado.Identifier.Insert(1, $"UnionTable{querys.Count}");
            for (int i = 0; i < querys.Count; i++)
            {
                queryBuilder.UnionAll.Add(querys[i].ToString());
                queryBuilder.Parameters.Append(querys[i].QueryBuilder.Parameters);
            }
            return new Query<T, TDbOptions>(Ado, queryBuilder);
        }
        #endregion
    }
}
